import java.math.BigInteger;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class Wallet {

    /**
     * 收据
     */
    private Map<TransactionId, Transaction> receipt = new HashMap<>();

    /**
     * 付款记录
     */
    private Map<TransactionId, Transaction> pay = new HashMap<>();

    public static final String KEY_ALGORITHM = "RSA";

    public static final int KEY_SIZE = 2048;

    private String publicKey;

    private String privateKey;

    private String name;

    private long createTime;

    private String address;

    public String getPublicKey() {
        return publicKey;
    }

    public void setPublicKey(String publicKey) {
        this.publicKey = publicKey;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public long getCreateTime() {
        return createTime;
    }

    public void setCreateTime(long createTime) {
        this.createTime = createTime;
    }

    public String getPrivateKey() {
        return privateKey;
    }

    public void setPrivateKey(String privateKey) {
        this.privateKey = privateKey;
    }


    public synchronized static Wallet createWallet(String name) throws Exception {

        // 如果name存在，则不再继续添加
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        keyPairGenerator.initialize(KEY_SIZE);
        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        Wallet wallet = new Wallet();
        wallet.setPrivateKey(Base58.encode(new BigInteger(keyPair.getPrivate().getEncoded())));
        wallet.setPublicKey(Base58.encode(new BigInteger(keyPair.getPublic().getEncoded())));

        String address = Address.convertPubKeyToAddress(keyPair.getPublic().getEncoded());
        boolean check = Address.checkAddress(address);
        System.out.println("check===" + check);
        wallet.setAddress(address);

        wallet.setName(name);
        wallet.setCreateTime(System.currentTimeMillis());

        return wallet;
    }

    public byte[] getPrivateKeyByte() throws Exception {
        BigInteger privateB = Base58.decode1(privateKey);
        return privateB.toByteArray();
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public byte[] getPublicKeyByte() {
        try {
            return Base58.decode1(publicKey).toByteArray();
        } catch (Exception e) {
            return null;
        }
    }

    public synchronized void addToMyReceipt(byte[] hash, Transaction transaction) {
        receipt.put(new TransactionId(hash), transaction);
    }

    public synchronized void addPayLog(byte[] hash, Transaction transaction) {
        pay.put(new TransactionId(hash), transaction);
    }


    public synchronized boolean pay(String address, int amount, NetWork netWork) throws Exception {

        try {
            boolean check = Address.checkAddress(address);
            if (!check) {
                System.out.println("address校验失败");
                return false;
            }
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }


        List<Input> preTransaction = new ArrayList<>();

        int vin = 0;


        Iterator<Map.Entry<TransactionId, Transaction>> iterator = receipt.entrySet().iterator();

        while (iterator.hasNext()) {

            Transaction transaction = iterator.next().getValue();
            int mine = transaction.getMine(getPublicKeyByte());
            if (mine <= 0) {
                System.out.println("this is not my receipt");
                continue;
            }

            vin = vin + mine;
            preTransaction.add(new Input(transaction.getHash(), mine));
            iterator.remove();
            if (vin >= amount) {
                break;
            }
        }

        if (vin < amount) {
            return false;
        }

        Transaction transaction = new Transaction();
        transaction.setInputs(preTransaction);

        Cost cost = new Cost();
        cost.setAddress(address);
        cost.setAmount(amount);
        transaction.setCosts(Collections.singletonList(cost));

        int v = vin - amount;
        if (v > 0) {
            Cost cost1 = new Cost();
            cost1.setAddress(this.getAddress());
            cost1.setAmount(v);
            transaction.setCosts(Arrays.asList(cost, cost1));
        } else {
            transaction.setCosts(Arrays.asList(cost));
        }

        byte[] ser = transaction.serialize(this);
        byte[] hash = Hash256.hash(ser);
        transaction.setHash(hash);

        TransactionId transactionId = new TransactionId(hash);

        pay.put(transactionId, transaction);
        // 未确认的订单不允许再次支付
        // receipt.put(hashKey, transaction);
        netWork.broadCastTransaction(ser);

        return true;
    }


    public synchronized void printInfo() {

        System.out.println("-----------my receipt-------------");
        for (Map.Entry<TransactionId, Transaction> item : receipt.entrySet()) {
            System.out.println(item.getValue().getTransactionInfo(address));
        }

        System.out.println("-----------my pay log-------------");
        for (Map.Entry<TransactionId, Transaction> item : pay.entrySet()) {
            System.out.println(item.getValue().getTransactionInfo(address));
        }
    }
}
